import { Node, UITransform, view, instantiate, screen, isValid } from "cc";
import BaseView, { IViewParams } from "../mvc/BaseView";
import { ResolutionPolicy } from "cc";
import ResMgr from "./ResMgr";
import { BaseMgr } from "../base/BaseMgr";
import { ControllerMgr } from "./ControllerMgr";
import { DataMgr } from "./DataMgr";
import { Camera } from "cc";
import { PREVIEW } from "cc/env";
import { director } from "cc";

interface ViewData {
	module: number|string,
	path: string,
	viewNode: Node,
	isOpen: boolean,
}

export enum EUIEvents {
	/** ui被打开 */
	Open = "UI_Open",
	Close = "UI_Close",
	CloseAll = "UI_CloseAll",
}

export default class UIMgr extends BaseMgr {
	public static get inst() {
		return this.getInstance();
	}

	/** ui配置 */
	private config: IUIConfig = null;
	/** 根结点 */
	private root: Node = null;
	/** ui摄像机 */
	private uiCamera: Camera = null;
	/** 视图表 */
	views: { [module: number]: ViewData } = {};

	loadingResModuleKey = -1;

	showLoading: () => void = null;
	hideLoading: () => void = null;

	/** 初始化 */
	setup(root: Node, cfg: IUIConfig, uiCamera: Camera) {
		this.root = root;
		this.config = cfg;
		this.uiCamera = uiCamera;
		screen.on(`window-resize`, this.onWindowResize, this);
		this.onWindowResize();
	}

	async open(module: number|string, params?: IViewParams, isShowLoading = true) {
		if (module === 0 || module === "" || module === undefined) return;
		let viewData = this.views[module];
		if (viewData) {
			if (isValid(viewData.viewNode)) {
				this.openSuccess(viewData, params, false);
				return;
			}
		} else {
			if (!this.config) {
				console.error(`ui config not exists, please call gfw.ui.init(root, config)`);
				return;
			}
			const cfg = this.config[module];
			if (!cfg) {
				console.error(`ui config not exists: ${module}`);
			}
			const path = cfg.prefabPath;
			const vd: ViewData = {
				path,
				viewNode: null,
				isOpen: true,
				module,
			};
			this.views[module] = vd;
			let loadingFlag = 0;
			if (isShowLoading) {
				setTimeout(() => {
					if (loadingFlag == 0) {
						this.showLoading && this.showLoading();
						loadingFlag = 1;
					}
				}, 500);
			}
			const prefab = await ResMgr.inst.loadPrefab(path, cfg.bundle);
			if (isShowLoading) {
				if (loadingFlag == 1) {
					this.hideLoading && this.hideLoading();
				}
				loadingFlag = 2;
			}
			if (!prefab) {
				console.error(`ui config not exists: ${module}`);
			} else {
				const viewNode = instantiate(prefab);
				vd.viewNode = viewNode;
				if (vd) {
					if (!vd.isOpen) {
						this.close(module);
					}
					else {
						this.openSuccess(vd, params);
					}
				}
				return;
			}
		}
		return;
	}

	openSuccess(vd: ViewData, params?: IViewParams, isNewOpen = true) {
		// console.log("open ui", vd.path)
		const viewNode = vd.viewNode;
		const viewCom = viewNode.getComponent(BaseView);
		const viewUTF = viewNode.getComponent(UITransform);
		viewNode['__module'] = vd.module;
		viewCom.module = vd.module;
		viewNode.parent = this.root;
		viewNode.active = true;
		if (viewCom) {
			viewCom.param = params;
			viewCom.ctrl = ControllerMgr.inst.get(vd.module);
			viewCom.data = DataMgr.inst.get(vd.module);
			viewCom.onBtnClose = (e, isDel) => this.close(vd.module, isDel);
			viewCom.onOpen(params);
		}
		if (params) {
			if (params.closeModuleWhenOpen !== undefined) {
				if (params.closeModuleWhenOpen > -1) {
					this.close(params.closeModuleWhenOpen);
				}
			}
			params.opened && params.opened();
		}
		viewUTF.setContentSize(view.getVisibleSize());

		const cs = this.root.children.filter(c => !!this.views[c[`__module`]]);
		cs.sort((a: Node, b: Node) => {
			const za = this.config[a[`__module`]].layer;
			const zb = this.config[b[`__module`]].layer;
			return za - zb;
		})
		cs.forEach((n, idx) => {
			n.setSiblingIndex(idx);
		})
		this.views[vd.module] = vd;

		if (vd.module != this.loadingResModuleKey) {
			this.close(this.loadingResModuleKey);
		}
		director.emit(EUIEvents.Open, vd.module);
	}

	getView<T extends BaseView>(module: number) {
		const viewData = this.views[module];
		let view: BaseView = null;
		if (viewData && isValid(viewData.viewNode)) {
			view = viewData.viewNode.getComponent(BaseView);
		}
		return view as T;
	}

	isOpen(module: number|string) {
	    const viewData = this.views[module];
		return viewData && viewData.isOpen;
	}

	close(module: number|string, isDel: boolean = true, emitEvent = true) {
		const viewData = this.views[module];
		if (viewData) {
			viewData.isOpen = false;
			const viewNode = viewData.viewNode;
			if (isValid(viewNode)) {
				// console.log('close ui', viewData.path, isDel)   // 关闭UI
				viewNode.getComponent(BaseView)?.ctrl?.onUIClose();
				if (isDel) {
					viewNode.destroy();
					delete this.views[module];
				} else {
					viewNode.active = false;
				}
				director.emit(EUIEvents.Close, module);
			}
		}
	}

	 

	closeAll(module: number = -1, isDel: boolean = true) {
		for (let i in this.views) {
		    const viewData = this.views[i];
			if (viewData.module != module) {
				this.close(viewData.module, isDel, false);
			}
		}
		director.emit(EUIEvents.CloseAll);
	}

	destroy(): void {
		this.views = {};
		screen.off(`window-resize`, this.onWindowResize, this);
	}

	onWindowResize() {
		let visibleSise = view.getVisibleSize();
		const resolutionSize = view.getDesignResolutionSize();
		const ratio = visibleSise.width / visibleSise.height;
		const designRatio = resolutionSize.width / resolutionSize.height;
		let rp = ResolutionPolicy.FIXED_HEIGHT
		if (ratio <= designRatio) {
			rp = ResolutionPolicy.FIXED_WIDTH
		}
		view.setResolutionPolicy(rp)
		visibleSise = view.getVisibleSize();
		this.uiCamera.orthoHeight = visibleSise.height / 2;

		for (let i in this.views) {
			const v = this.views[i];
			if (isValid(v.viewNode)) {
				v.viewNode.getComponent(UITransform).setContentSize(visibleSise)
			}
		}
	}

	loadingRes(dirName: string, bundleName?: string) {
		return new Promise((resolve, reject) => {
			if (PREVIEW) {
				resolve(null);
				return;
			}
			this.open(this.loadingResModuleKey).then(() => {
				const view = this.getView(this.loadingResModuleKey);
				ResMgr.inst.loadDir(dirName, (finished, total) => {
					const p = finished / total
					if (view) {
						view.setProgress(p)
					}
				}, bundleName).then(resolve);
			});
		});
	}

	closeLoadingRes() {
		this.close(this.loadingResModuleKey);
	}
}

